package middleware

import (
	"crypto/md5"
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	models "image-server/dao"
	"image-server/dao/model"
	response "image-server/models"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/gin-gonic/gin"
)

// 配置参数（金山开发者信息）
var (
	AccessID    = "AK20230629HENRRS"
	SecretKey   = "d99b7a674c459896ea89b2dcf74d742f"
	ProxyAddr   = "http://127.0.0.1:8899" // 固定代理
	AuthURL     = "https://account.wps.cn/api/v3/islogin"
	AuthURI     = "/api/v3/islogin"
	UserInfoURL = "https://account.wps.cn/api/user/" // 金山用户信息接口
)

// 上下文键（存储用户信息）
type contextKey string

const (
	WPSUserKey contextKey = "wps_user" // 存储完整用户信息
)

// 金山登录状态响应结构体
type WPSLoginStatus struct {
	Result string `json:"result"` // "ok"表示登录有效
	UserID int64  `json:"userid"` // 金山用户ID
}

// 金山用户信息结构体（核心字段）
type WPSUserInfo struct {
	Userid   int64  `json:"userid"`   // 金山用户ID
	Nickname string `json:"nickname"` // 用户昵称
	Result   string `json:"result"`   // 接口状态
}

// Auth 金山登录认证中间件：验证并获取用户信息存入上下文
func Auth() gin.HandlerFunc {
	return func(c *gin.Context) {
		//1. 获取wps_sid（优先Cookie，其次请求头）
		wpsSID, err := c.Cookie("wps_sid")
		if err != nil || wpsSID == "" {
			wpsSID = c.GetHeader("X-WPS-SID")
		}

		//wpsSID := c.GetHeader("wps_sid")

		if wpsSID == "" {
			c.JSON(http.StatusUnauthorized, gin.H{
				"code": 401,
				"msg":  "未检测到登录状态，请先通过金山登录",
			})
			c.Abort()
			return
		}

		// 2. 验证登录状态并获取用户信息
		user, err := verifyAndGetUser(wpsSID)
		if err != nil {
			response.Error(c.Writer, http.StatusInternalServerError, "登录验证失败: "+err.Error())
			c.Abort()
			return
		}
		if user == nil {
			c.SetCookie("wps_sid", "", -1, "/", "", false, true) // 清除无效Cookie
			response.Error(c.Writer, http.StatusInternalServerError, "登录已失效，请重新登录")
			c.Abort()
			return
		}
		// 3. 检查数据库中是否已存在该用户，不存在则新增
		// 假设user结构体中包含WpsUID（金山用户唯一标识）
		exists, err := models.CheckUserExists(user.WpsUID)
		if err != nil {
			response.Error(c.Writer, http.StatusInternalServerError, "用户信息校验失败: "+err.Error())
			c.Abort()
			return
		}
		// 不存在则存储到数据库
		if !exists {
			err := models.SaveUserToDB(user)
			if err != nil {
				response.Error(c.Writer, http.StatusInternalServerError, "用户信息存储失败: "+err.Error())
				c.Abort()
				return
			}
		}
		fmt.Println("world,", user)
		//从这里去查询数据库hu
		newUser, _ := models.GetUserByWpsUID(user.WpsUID)
		fmt.Println(newUser)
		// 3. 存储完整用户信息到上下文
		c.Set(string(WPSUserKey), newUser)
		c.Next()
	}
}

// GetUser 从上下文获取完整用户信息
func GetUser(c *gin.Context) (*model.User, error) {
	userVal, ok := c.Get(string(WPSUserKey))
	fmt.Println("hello,", userVal)
	if !ok {
		return nil, errors.New("未获取到用户信息")
	}

	user, ok := userVal.(*model.User)
	if !ok {
		return nil, errors.New("用户信息格式错误")
	}

	return user, nil
}

// 验证登录状态并获取完整用户信息
func verifyAndGetUser(wpsSID string) (*model.User, error) {
	// 创建HTTP客户端
	client, err := createProxyClient(ProxyAddr)
	if err != nil {
		return nil, fmt.Errorf("创建客户端失败: %w", err)
	}

	// 1. 验证会话有效性，获取用户ID
	loginStatus, err := checkLoginStatus(client, wpsSID)
	if err != nil {
		return nil, fmt.Errorf("验证会话失败: %w", err)
	}
	if loginStatus.Result != "ok" {
		return nil, nil // 会话无效
	}

	// 2. 根据用户ID获取详细信息
	userInfo, err := getUserInfo(client, wpsSID, loginStatus.UserID)
	if err != nil {
		return nil, fmt.Errorf("获取用户信息失败: %w", err)
	}
	if userInfo.Result != "ok" {
		return nil, fmt.Errorf("用户信息接口返回错误: %s", userInfo.Result)
	}

	// 3. 封装完整用户信息
	return &model.User{
		WpsUID:   fmt.Sprintf("%d", loginStatus.UserID), // 转为字符串作为唯一标识
		Username: userInfo.Nickname,
	}, nil
}

// 验证登录状态
func checkLoginStatus(client *http.Client, wpsSID string) (*WPSLoginStatus, error) {
	date := time.Now().UTC().Format(time.RFC1123)
	contentMD5 := genContentMD5(AuthURI)
	authorization := genAuthorization(AuthURI, date)

	req, err := http.NewRequest("GET", AuthURL, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", authorization)
	req.Header.Set("Content-Md5", contentMD5)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Date", date)
	req.Header.Set("Cookie", fmt.Sprintf("wps_sid=%s", wpsSID))

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("验证接口状态码异常: %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var status WPSLoginStatus
	if err := json.Unmarshal(body, &status); err != nil {
		return nil, fmt.Errorf("解析登录状态失败: %w, 内容: %s", err, string(body))
	}

	return &status, nil
}

// 获取用户详细信息
func getUserInfo(client *http.Client, wpsSID string, userID int64) (*WPSUserInfo, error) {
	uri := fmt.Sprintf("/api/user/%d", userID)
	fullURL := UserInfoURL + fmt.Sprintf("%d", userID)

	date := time.Now().UTC().Format(time.RFC1123)
	contentMD5 := genContentMD5(uri)
	authorization := genAuthorization(uri, date)

	req, err := http.NewRequest("GET", fullURL, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", authorization)
	req.Header.Set("Content-Md5", contentMD5)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Date", date)
	req.Header.Set("Cookie", fmt.Sprintf("wps_sid=%s", wpsSID))

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("用户信息接口状态码异常: %d", resp.StatusCode)
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var info WPSUserInfo
	if err := json.Unmarshal(body, &info); err != nil {
		return nil, fmt.Errorf("解析用户信息失败: %w, 内容: %s", err, string(body))
	}

	// 校验用户ID一致性（防篡改）
	if info.Userid != userID {
		return nil, errors.New("用户信息不一致，可能存在安全风险")
	}

	return &info, nil
}

// 生成Content-MD5
func genContentMD5(uri string) string {
	sum := md5.Sum([]byte(uri))
	return hex.EncodeToString(sum[:])
}

// 生成Authorization签名
func genAuthorization(uri, date string) string {
	contentMD5 := genContentMD5(uri)
	signStr := SecretKey + contentMD5 + "application/json" + date

	h := sha1.New()
	h.Write([]byte(signStr))
	signature := hex.EncodeToString(h.Sum(nil))

	return fmt.Sprintf("WPS-2:%s:%s", AccessID, signature)
}

// 创建带代理的HTTP客户端
func createProxyClient(proxyAddr string) (*http.Client, error) {
	if proxyAddr == "" {
		return &http.Client{Timeout: 10 * time.Second}, nil
	}
	proxyURL, err := url.Parse(proxyAddr)
	if err != nil {
		return nil, fmt.Errorf("代理地址无效: %w", err)
	}
	return &http.Client{
		Transport: &http.Transport{Proxy: http.ProxyURL(proxyURL)},
		Timeout:   10 * time.Second,
	}, nil
}
